﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Data;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Windows.Controls.Primitives;
using System.Globalization;

namespace Gmantis.Controls
{
    public static class Extensions
    {
        // Fields
        private static object _bindingMarker;

        static Extensions()
        {
            _bindingMarker = new object();
        }

        public static GeneralTransform GmTransformToVisual(this UIElement element, UIElement visual)
        {
            try
            {
                return element.TransformToVisual(visual);
            }
            catch
            {
                return new MatrixTransform { Matrix = Matrix.Identity };
            }
        }

        public static Binding From<T>(this Binding binding, T source, Expression<Func<T, object>> expr)
        {
            binding.Source = source;
            binding.Path = new PropertyPath(GetPropertyPath<T>(expr), new object[0]);
            return binding;
        }

        internal static string GetPropertyPath<T>(Expression<Func<T, object>> expression)
        {
            MemberExpression expression3;
            IList<string> source = new List<string>();
            for (System.Linq.Expressions.Expression expression2 = expression.Body; expression2.NodeType != ExpressionType.Parameter; expression2 = expression3.Expression)
            {
                ExpressionType nodeType = expression2.NodeType;
                if (nodeType == ExpressionType.Call)
                {
                    MethodCallExpression expression4 = (MethodCallExpression)expression2;
                    throw new InvalidOperationException("The member '" + expression4.Method.Name + "' is a method call but a Property or Field was expected.");
                }
                if ((nodeType != ExpressionType.Convert) && (nodeType != ExpressionType.MemberAccess))
                {
                    throw new InvalidOperationException("The expression NodeType '" + expression2.NodeType.ToString() + "' is not supported, expected MemberAccess, Convert, or Call.");
                }
                expression3 = (expression2.NodeType == ExpressionType.MemberAccess) ? ((MemberExpression)expression2) : ((MemberExpression)((UnaryExpression)expression2).Operand);
                if ((expression3.Member.MemberType != MemberTypes.Property) && (expression3.Member.MemberType != MemberTypes.Field))
                {
                    throw new InvalidOperationException("The member '" + expression3.Member.Name + "' is a " + expression3.Member.MemberType.ToString() + " but a Property or Field is expected.");
                }
                source.Add(expression3.Member.Name);
            }
            return string.Join(".", source.Reverse<string>().ToArray<string>());
        }

        internal static Panel GetItemsHost(this ItemsControl control)
        {
            if (control == null)
                throw new ArgumentNullException("control");

            DependencyObject reference = control.ItemContainerGenerator.ContainerFromIndex(0);
            if (reference != null)
                return (VisualTreeHelper.GetParent(reference) as Panel);

            FrameworkElement node = control.GetVisualChildren().FirstOrDefault<FrameworkElement>();
            if (node != null)
            {
                ItemsPresenter presenter = node.GetLogicalDescendents().OfType<ItemsPresenter>().FirstOrDefault<ItemsPresenter>();
                if (presenter != null && VisualTreeHelper.GetChildrenCount(presenter) > 0)
                    return (VisualTreeHelper.GetChild(presenter, 0) as Panel);
            }

            return null;
        }

        internal static IEnumerable<FrameworkElement> GetVisualChildren(this FrameworkElement node)
        {
            int childIndex = 0;
            while (true)
            {
                if (childIndex >= VisualTreeHelper.GetChildrenCount(node))
                    yield break;

                yield return (VisualTreeHelper.GetChild(node, childIndex) as FrameworkElement);
                childIndex++;
            }
        }

        internal static IEnumerable<FrameworkElement> GetLogicalDescendents(this FrameworkElement node)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(node); i++)
            {
                FrameworkElement child = VisualTreeHelper.GetChild(node, i) as FrameworkElement;
                yield return child;
                foreach (FrameworkElement iteratorVariable2 in child.GetLogicalDescendents())
                {
                    yield return iteratorVariable2;
                }
            }
        }

        internal static bool Contains(this Style style, DependencyProperty dependencyProperty)
        {
            foreach (Setter setter in style.Setters.OfType<Setter>())
            {
                if (setter.Property == dependencyProperty)
                {
                    return true;
                }
            }
            return false;
        }

        public static void SetBinding<T>(this FrameworkElement target, DependencyProperty dp, T source, Expression<Func<T, object>> expr)
        {
            target.SetBinding(dp, source, GetPropertyPath<T>(expr), null);
        }

        internal static void SetBinding(this FrameworkElement target, DependencyProperty dp, object source, string sourcePropertyName, IValueConverter converter)
        {
            object objA = target.ReadLocalValue(dp);
            BindingExpression expression = objA as BindingExpression;
            if (object.ReferenceEquals(objA, DependencyProperty.UnsetValue) || ((expression != null) && object.ReferenceEquals(expression.ParentBinding.ConverterParameter, _bindingMarker)))
            {
                Binding binding = new Binding(sourcePropertyName)
                {
                    Source = source,
                    Converter = converter,
                    ConverterParameter = _bindingMarker
                };
                target.SetBinding(dp, binding);
                if ((target.Style != null) && target.Style.Contains(dp))
                {
                    target.ClearValue(dp);
                }
            }
        }

        public static IEnumerable<UIElement> HitTest(this UIElement element, Point intersectingPoint)
        {
            try
            {
                return VisualTreeHelper.FindElementsInHostCoordinates(intersectingPoint, element);
            }
            catch (Exception)
            {
                return new List<UIElement>();
            }
        }

        internal static bool TryCaptureMouse(this UIElement element)
        {
            return element.CaptureMouse();
        }

        public static IEnumerable<UIElement> HitTestProjectionWorkaround(this UIElement element, Point intersectingPoint)
        {
            UIElement topMostParent = element.GetTopMostParent();
            return VisualTreeHelper.FindElementsInHostCoordinates(intersectingPoint, topMostParent);
        }

        public static UIElement GetTopMostParent(this UIElement element)
        {
            DependencyObject obj3;
            DependencyObject reference = element;
        Label_0002:
            obj3 = VisualTreeHelper.GetParent(reference);
            if (obj3 is UIElement)
            {
                reference = (UIElement)obj3;
                goto Label_0002;
            }
            if (obj3 != null)
            {
                reference = obj3;
                goto Label_0002;
            }
            FrameworkElement element2 = reference as FrameworkElement;
            if ((element2 != null) && (element2.Parent is Popup))
            {
                reference = element2.Parent;
                goto Label_0002;
            }
            return (reference as UIElement);
        }

        public static Orientation GetOrientation(this Dock dock)
        {
            switch (dock)
            {
                case Dock.Left:
                case Dock.Right:
                    return Orientation.Horizontal;

                case Dock.Top:
                case Dock.Bottom:
                    return Orientation.Vertical;
            }
            throw new ArgumentException();
        }

        public static Orientation Opposite(this Orientation value)
        {
            if (value == Orientation.Horizontal)
            {
                return Orientation.Vertical;
            }
            return Orientation.Horizontal;
        }

        internal static Size ActualSize(this FrameworkElement element)
        {
            return new Size(element.ActualWidth, element.ActualHeight);
        }

        public static GeneralTransform GTransformToVisual(this UIElement element, UIElement visual)
        {
            try
            {
                return element.TransformToVisual(visual);
            }
            catch
            {
                return new MatrixTransform { Matrix = Matrix.Identity };
            }
        }

        internal static Rect GetAccumulatedClip(this FrameworkElement element, Predicate<FrameworkElement> clipParentRenderSize = null)
        {
            Point location = new Point();
            try
            {
                location = element.GTransformToVisual(element.GetRootVisual()).Transform(new Point(0.0, 0.0));
            }
            catch (ArgumentException)
            {
                return new Rect();
            }
            return element.GetAccumulatedClip(location, clipParentRenderSize);
        }

        internal static Rect GetAccumulatedClip(this FrameworkElement element, Point location, Predicate<FrameworkElement> clipParentRenderSize = null)
        {
            //Rect rect;
            Rect accumClip = new Rect(0.0, 0.0, DomUtil.SilverlightSize.Width, DomUtil.SilverlightSize.Height);
            Action<Rect, Point> action = delegate(Rect clip, Point local)
            {
                clip.X += local.X;
                clip.Y += local.Y;
                double num = Math.Min(accumClip.Right, clip.Right);
                double num2 = Math.Min(accumClip.Bottom, clip.Bottom);
                accumClip.X = Math.Max(accumClip.X, clip.X);
                accumClip.Y = Math.Max(accumClip.Y, clip.Y);
                accumClip.Width = Math.Max((double)(num - accumClip.X), (double)0.0);
                accumClip.Height = Math.Max((double)(num2 - accumClip.Y), (double)0.0);
            };
            List<FrameworkElement> parentsPopup = element.GetParentsPopup();
            foreach (FrameworkElement element2 in parentsPopup)
            {
                Point point2 = new Point();
                Point point = element2.GTransformToVisual(parentsPopup.Last<FrameworkElement>()).Transform(point2);
                if (element2.Clip != null)
                {
                    action(element2.Clip.Bounds, point);
                }
                Geometry layoutClip = LayoutInformation.GetLayoutClip(element2);
                if (layoutClip != null)
                {
                    action(layoutClip.Bounds, point);
                }
                if ((clipParentRenderSize == null) || clipParentRenderSize(element2))
                {
                    action(new Rect(0.0, 0.0, element2.RenderSize.Width, element2.RenderSize.Height), point);
                }
            }
            //return new Rect { X = accumClip.X - location.X, Y = accumClip.Y - location.Y, Width = ((accumClip.Width + accumClip.X) - location.X) - rect.X, Height = ((accumClip.Height + accumClip.Y) - location.Y) - rect.Y };
            return new Rect { X = accumClip.X - location.X, Y = accumClip.Y - location.Y, Width = ((accumClip.Width + accumClip.X) - location.X), Height = ((accumClip.Height + accumClip.Y) - location.Y) };
        }

        internal static List<FrameworkElement> GetParentsPopup(this FrameworkElement current)
        {
            List<FrameworkElement> list = new List<FrameworkElement>();
        Label_0006:
            if (current != null)
            {
                list.Add(current);
                FrameworkElement parent = VisualTreeHelper.GetParent(current) as FrameworkElement;
                if (parent != null)
                {
                    current = parent;
                    goto Label_0006;
                }
                Popup popup = current.Parent as Popup;
                if ((popup != null) && popup.IsOpen)
                {
                    current = (FrameworkElement)popup.Parent;
                    goto Label_0006;
                }
            }
            return list;
        }

        public static void MakeRelative(this Popup popup, FrameworkElement relativeElement, Func<List<AnchorMatching>> getLocations)
        {
            new RelativePopup(relativeElement, getLocations, popup);
        }

        internal static void MakeRelative(this Popup popup, FrameworkElement relativeElement, Func<List<AnchorMatching>> getLocations, bool ignoreFlowDirection)
        {
            new RelativePopup(relativeElement, getLocations, popup, ignoreFlowDirection);
        }

        public static void MakeRelative(this Popup popup, FrameworkElement relativeElement, Func<List<AnchorMatching>> getLocations, Func<Point> getPosition, Func<Point> getReferencePosition)
        {
            new RelativePopup(relativeElement, getLocations, getPosition, getReferencePosition, popup);
        }

        internal static void MakeRelative(this Popup popup, FrameworkElement relativeElement, Func<List<AnchorMatching>> getLocations, Func<Point> getPosition, Func<Point> getReferencePosition, bool ignoreFlowDirection)
        {
            new RelativePopup(relativeElement, getLocations, getPosition, getReferencePosition, popup, ignoreFlowDirection);
        }

        public static IEnumerable<FrameworkElement> GetParents(this FrameworkElement elem, bool includeSelf = true)
        {
            if (elem != null)
            {
                if (includeSelf)
                {
                    yield return elem;
                }
                FrameworkElement reference = elem;
                while ((reference = VisualTreeHelper.GetParent(reference) as FrameworkElement) != null)
                {
                    yield return reference;
                }
            }
        }

        public static IList<FrameworkElement> GetAllParents(this FrameworkElement elem)
        {
            List<FrameworkElement> list = new List<FrameworkElement>();
            if (elem != null)
            {
                FrameworkElement reference = elem;
                while (VisualTreeHelper.GetParent(reference) is FrameworkElement)
                {
                    reference = VisualTreeHelper.GetParent(reference) as FrameworkElement;
                    list.Add(reference);
                }
            }
            return list;
        }

        internal static IEnumerable<T> GetAllChildren<T>(this FrameworkElement elem) where T : FrameworkElement
        {
            if (elem == null)
            {
                yield return default(T);
            }
            if (elem is T)
            {
                yield return (elem as T);
            }
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(elem); i++)
            {
                FrameworkElement child = VisualTreeHelper.GetChild(elem, i) as FrameworkElement;
                if (child != null)
                {
                    foreach (T iteratorVariable2 in child.GetAllChildren<T>())
                    {
                        yield return iteratorVariable2;
                    }
                }
            }
        }

        internal static void OnRootVisualSetted(Action a)
        {
            Action tryRoot = null;
            tryRoot = delegate
            {
                if (Application.Current.RootVisual != null)
                {
                    a();
                }
                else
                {
                    new Grid().Dispatcher.BeginInvoke(tryRoot);
                }
            };
            tryRoot();
        }

        internal static bool ToBool(this Visibility v)
        {
            if (v != Visibility.Visible)
            {
                return false;
            }
            return true;
        }

        public static Type GetNonNullableType(this Type type)
        {
            if (type.IsNullableType())
            {
                return type.GetGenericArguments()[0];
            }
            return type;
        }

        public static bool IsNullableType(this Type type)
        {
            return (((type != null) && type.IsGenericType) && (type.GetGenericTypeDefinition() == typeof(Nullable<>)));
        }

        internal static object GetPropertyValue(this object target, string path)
        {
            Type customType;
            if (target == null)
            {
                return null;
            }
            if (string.IsNullOrEmpty(path))
            {
                return target;
            }
            if (path.Contains("."))
            {
                return target.GetPropertyValue(path.Substring(0, path.IndexOf('.'))).GetPropertyValue(path.Substring(path.IndexOf('.') + 1));
            }
            if (path.Contains("["))
            {
                int num;
                string str = path.Substring(0, path.IndexOf('['));
                string s = path.Substring(path.IndexOf('[') + 1, (path.IndexOf(']') - path.IndexOf('[')) - 1);
                if (int.TryParse(s, out num))
                {
                    if (string.IsNullOrEmpty(str))
                    {
                        return target.GetType().GetProperty("Item", null, new Type[] { typeof(int) }).GetValue(target, new object[] { num });
                    }
                    return target.GetPropertyValue(str).GetPropertyValue(path.Substring(path.IndexOf('['), (path.IndexOf(']') - path.IndexOf('[')) + 1));
                }
                if (string.IsNullOrEmpty(str))
                {
                    return target.GetType().GetProperty("Item", null, new Type[] { typeof(string) }).GetValue(target, new object[] { s });
                }
                return target.GetPropertyValue(str).GetPropertyValue(path.Substring(path.IndexOf('['), (path.IndexOf(']') - path.IndexOf('[')) + 1));
            }
            if (target is ICustomTypeProvider)
            {
                customType = (target as ICustomTypeProvider).GetCustomType();
            }
            else
            {
                customType = target.GetType();
            }
            PropertyInfo property = customType.GetProperty(path);
            if (property == null)
            {
                return null;
            }
            return property.GetValue(target, null);
        }

        public static T GetPropertyValue<T>(this object target, string path)
        {
            if (target == null)
            {
                return default(T);
            }
            if (string.IsNullOrEmpty(path))
            {
                if (target is T)
                {
                    return (T)target;
                }
                return default(T);
            }
            object propertyValue = target.GetPropertyValue(path);
            if (propertyValue == null)
            {
                return default(T);
            }
            return (T)propertyValue;
        }

        internal static void Bind(FrameworkElement source, DependencyProperty sourceDProperty, FrameworkElement target, string targetPropertyPath)
        {
            Binding binding = new Binding(targetPropertyPath)
            {
                Mode = BindingMode.TwoWay,
                Source = target
            };
            source.SetBinding(sourceDProperty, binding);
        }

        public static void BindInputMethodProperties(this FrameworkElement element, FrameworkElement source)
        {
            Binding binding = new Binding
            {
                Source = source,
                Path = new PropertyPath(InputMethod.IsInputMethodEnabledProperty)
            };
            element.SetBinding(InputMethod.IsInputMethodEnabledProperty, binding);
            binding = new Binding
            {
                Source = source,
                Path = new PropertyPath(InputMethod.PreferredImeStateProperty)
            };
            element.SetBinding(InputMethod.PreferredImeStateProperty, binding);
        }

        internal static bool IsVisualOrLogicalParentOf(this DependencyObject parent, DependencyObject descendant)
        {
            while (descendant != null)
            {
                if (descendant == parent)
                {
                    return true;
                }
                descendant = descendant.GetVisualOrLogicalParent();
            }
            return false;
        }

        internal static DependencyObject GetVisualOrLogicalParent(this DependencyObject obj)
        {
            DependencyObject parent = VisualTreeHelper.GetParent(obj);
            if (parent != null)
            {
                return parent;
            }
            FrameworkElement element = obj as FrameworkElement;
            if (element == null)
            {
                return null;
            }
            return element.Parent;
        }

        public static bool ContainsAny<T>(this List<T> list, params T[] values)
        {
            bool flag = false;
            foreach (T local in values)
            {
                flag = list.Contains(local);
                if (flag)
                {
                    return flag;
                }
            }
            return flag;
        }

        internal static void RemoveFromParent(this FrameworkElement elem)
        {
            if (elem != null)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(elem);
                if (parent != null)
                {
                    if (parent is Panel)
                    {
                        (parent as Panel).Children.Remove(elem);
                    }
                    else if (parent is ContentControl)
                    {
                        (parent as ContentControl).Content = null;
                    }
                    else if (parent is ContentPresenter)
                    {
                        (parent as ContentPresenter).Content = null;
                    }
                    else if (parent is ItemsControl)
                    {
                        (parent as ItemsControl).Items.Remove(elem);
                    }
                    else if (parent is Border)
                    {
                        (parent as Border).Child = new Border();
                    }
                    else
                    {
                        if (!(parent is Popup))
                        {
                            throw new ArgumentException(string.Format("Cannot remove an element from a {0}", elem.Parent.GetType().Name));
                        }
                        (parent as Popup).Child = null;
                    }
                }
            }
        }

        internal static Visibility ToVisibility(this bool b)
        {
            if (!b)
            {
                return Visibility.Collapsed;
            }
            return Visibility.Visible;
        }

        internal static Panel GetItemsHost(this System.Windows.Controls.ItemsControl control)
        {
            if (control == null)
            {
                throw new ArgumentNullException("control");
            }
            DependencyObject reference = control.ItemContainerGenerator.ContainerFromIndex(0);
            if (reference != null)
            {
                return (VisualTreeHelper.GetParent(reference) as Panel);
            }
            FrameworkElement node = control.GetVisualChildren().FirstOrDefault<FrameworkElement>();
            if (node != null)
            {
                ItemsPresenter presenter = node.GetLogicalDescendents().OfType<ItemsPresenter>().FirstOrDefault<ItemsPresenter>();
                if ((presenter != null) && (VisualTreeHelper.GetChildrenCount(presenter) > 0))
                {
                    return (VisualTreeHelper.GetChild(presenter, 0) as Panel);
                }
            }
            return null;
        }

        public static T FindParent<T>(this FrameworkElement elem) where T : FrameworkElement
        {
            if (elem != null)
            {
                FrameworkElement reference = elem;
                while (VisualTreeHelper.GetParent(reference) is FrameworkElement)
                {
                    reference = VisualTreeHelper.GetParent(reference) as FrameworkElement;
                    if (reference is T)
                    {
                        return (reference as T);
                    }
                }
            }
            return default(T);
        }

        public static bool IsNumeric(this Type type)
        {
            if (((((type != typeof(double)) && (type != typeof(float))) && ((type != typeof(int)) && (type != typeof(uint)))) && (((type != typeof(long)) && (type != typeof(ulong))) && ((type != typeof(short)) && (type != typeof(ushort))))) && ((type != typeof(sbyte)) && (type != typeof(byte))))
            {
                return (type == typeof(decimal));
            }
            return true;
        }

        internal static void SetValue(this PropertyInfo propertyInfo, object target, object value, object[] index, IValueConverter converter, object converterParameter, CultureInfo culture)
        {
            if ((converter != null) && (propertyInfo.PropertyType != value.GetType()))
            {
                value = converter.Convert(value, propertyInfo.PropertyType, converterParameter, culture);
            }
            propertyInfo.SetValue(target, value, index);
        }

        public static void SetPropertyValue<T>(this object target, string path, T value, IValueConverter converter, object converterParameter, CultureInfo culture)
        {
            if ((target != null) && !string.IsNullOrEmpty(path))
            {
                if (path.Contains("."))
                {
                    target.GetPropertyValue(path.Substring(0, path.IndexOf('.'))).SetPropertyValue<T>(path.Substring(path.IndexOf('.') + 1), value, converter, converterParameter, culture);
                }
                else if (path.Contains("["))
                {
                    int num;
                    string str = path.Substring(0, path.IndexOf('['));
                    string s = path.Substring(path.IndexOf('[') + 1, (path.IndexOf(']') - path.IndexOf('[')) - 1);
                    if (int.TryParse(s, out num))
                    {
                        if (string.IsNullOrEmpty(str))
                        {
                            target.GetType().GetProperty("Item", null, new Type[] { typeof(int) }).SetValue(target, value, new object[] { num }, converter, converterParameter, culture);
                        }
                        else
                        {
                            target.GetPropertyValue(str).SetPropertyValue<T>(path.Substring(path.IndexOf('['), (path.IndexOf(']') - path.IndexOf('[')) + 1), value, converter, converterParameter, culture);
                        }
                    }
                    else if (string.IsNullOrEmpty(str))
                    {
                        target.GetType().GetProperty("Item", null, new Type[] { typeof(string) }).SetValue(target, value, new object[] { s }, converter, converterParameter, culture);
                    }
                    else
                    {
                        target.GetPropertyValue(str).SetPropertyValue<T>(path.Substring(path.IndexOf('['), (path.IndexOf(']') - path.IndexOf('[')) + 1), value, converter, converterParameter, culture);
                    }
                }
                else
                {
                    Type customType;
                    if (target is ICustomTypeProvider)
                    {
                        customType = (target as ICustomTypeProvider).GetCustomType();
                    }
                    else
                    {
                        customType = target.GetType();
                    }
                    customType.GetProperty(path).SetValue(target, value, null, converter, converterParameter, culture);
                }
            }
        }

        public static bool IsNumericIntegral(this Type type)
        {
            if ((((type != typeof(int)) && (type != typeof(uint))) && ((type != typeof(long)) && (type != typeof(ulong)))) && (((type != typeof(short)) && (type != typeof(ushort))) && (type != typeof(sbyte))))
            {
                return (type == typeof(byte));
            }
            return true;
        }

        public static bool IsNumericIntegralUnsigned(this Type type)
        {
            if (((type != typeof(uint)) && (type != typeof(ulong))) && (type != typeof(ushort)))
            {
                return (type == typeof(byte));
            }
            return true;
        }

        public static void SetBinding<T>(this FrameworkElement target, DependencyProperty dp, T source, Expression<Func<T, object>> expr, IValueConverter converter)
        {
            target.SetBinding(dp, source, GetPropertyPath<T>(expr), converter);
        }
    }
}
